//
// Created on 2022-09-05.
//
#include <cstddef>
#include <node_api.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <js_native_api_types.h>

#include "napi/native_api.h"
#include "serialport.h"
#include "FileUtil.h"

#include "common/plugin_common.h"

FileUtil _fileUtil;

struct CallbackContext {
    napi_env env = nullptr;
    napi_ref callbackRef = nullptr;
    int status = -1;
    napi_async_work worker = nullptr;  
    char retData[512] = {""};
};

static napi_value Open(napi_env env, napi_callback_info info)
{
    napi_status status;
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype;
    status = napi_typeof(env, args[0], &valuetype);
    if (status != napi_ok) {
        return nullptr;
    }
    if (valuetype != napi_string) {
        LOGE("Wrong arguments");
        return nullptr;
    }

    status = napi_typeof(env, args[1], &valuetype);
    if (status != napi_ok) {
        return nullptr;
    }
    if (valuetype != napi_number) {
        napi_throw_type_error(env, NULL, "Wrong arguments");
        return nullptr;
    }
    char port[MAX_SIZE] = { 0 };
    size_t portLen = 0;
    napi_get_value_string_utf8(env, args[0], port, MAX_SIZE, &portLen);
    double baudrate;
    napi_get_value_double(env, args[1], &baudrate);
    
    if (fd > 0) {
        napi_value sum;
        napi_create_double(env, fd, &sum);
        return sum;
    }
    fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK);

    /*---------- Setting the Attributes of the serial port using termios structure --------- */

    /* Create the structure                          */
    struct termios cfg;

    tcgetattr(fd, &cfg);	/* Get the current attributes of the Serial port */

    cfmakeraw(&cfg);
    cfsetispeed(&cfg,getBaudrate(baudrate)); /* 输入波特率 Set Read  Speed as 9600                       */
    cfsetospeed(&cfg,getBaudrate(baudrate)); /* 输出波特率 Set Write Speed as 9600                       */

    cfg.c_cflag &= ~PARENB;   /* 控制参数Disables the Parity Enable bit(PARENB),So No Parity   */
    cfg.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
    cfg.c_cflag &= ~CSIZE;	 /* Clears the mask for setting the data size             */
    cfg.c_cflag |=  CS8;      /* Set the data bits = 8                                 */

    cfg.c_cflag &= ~CRTSCTS;       /* No Hardware flow Control                         */
    cfg.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */


    cfg.c_iflag &= ~(IXON | IXOFF | IXANY);          /* 输入参数 Disable XON/XOFF flow control both i/p and o/p */
    cfg.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Non Cannonical mode                            */

    cfg.c_oflag &= ~OPOST;  /* 输出参数 No Output Processing*/

    /* Setting Time outs */
    cfg.c_cc[VMIN] = 0;    /* 控制字符 noncanonical mode中规定read()返回的的最小字节数*/
    cfg.c_cc[VTIME] = 5;    /* 0.5s延迟 noncanonical mode中规定read()返回的超时时间（100ms为单位） */

    if((tcsetattr(fd,TCSANOW,&cfg)) != 0) /* Set the attributes to the termios structure*/
        printf("\n  ERROR ! in Setting attributes");
    else
        printf("\n  BaudRate = 9600 \n  StopBits = 1 \n  Parity   = none");

    napi_value sum;
    napi_create_double(env, fd, &sum);
    return sum;
}

static napi_value Write(napi_env env, napi_callback_info info)
{
    int ret = -1;
    napi_status status;
    size_t argc = 1;
    napi_value args[1] = { nullptr };

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (fd > 0) {
        napi_valuetype valuetype;
        status = napi_typeof(env, args[0], &valuetype);
        if (status != napi_ok) {
            return nullptr;
        }
        if (valuetype != napi_string) {
            napi_throw_type_error(env, NULL, "Wrong arguments");
            return nullptr;
        }

        char value[MAX_SIZE] = { 0 };
        size_t valueLen;        
        napi_get_value_string_utf8(env, args[0], value, MAX_SIZE, &valueLen);
        //_fileUtil.WriteFile(env, value);
        ret = write(fd, value, valueLen);
    }
    napi_value result;
    napi_create_double(env, ret, &result);
    
    return result;

}

static napi_value Read(napi_env env, napi_callback_info info)
{
    int ret = -1;
    char read_buffer[MAX_SIZE] = { 0 };/* Buffer to store the data received              */
    size_t valueLen = MAX_SIZE;
    napi_value result;
    if (fd > 0) {
        //tcflush(fd, TCIFLUSH);   /* Discards old data in the rx buffer            */
        ret = read(fd,&read_buffer,valueLen); /* Read the data                   */        
    } else {
         return 0;
    } 
        
    if (ret > 0) {
        //LOGE("uart read len: %{public}d\n",ret);
        napi_create_string_utf8(env, read_buffer, ret, &result);
    } else {
        result = 0;
        napi_create_string_utf8(env, nullptr, 0, &result);
    }    
    return result;
}

static napi_value Close(napi_env env, napi_callback_info info)
{
    int ret = -1;
    if (fd > 0) {
        ret = close(fd);
        fd = -1;
    }
    napi_value sum;
    napi_create_double(env, ret, &sum);

    return sum;

}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {       
        { "open", nullptr, Open, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "write", nullptr, Write, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "read", nullptr, Read, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "close", nullptr, Close, nullptr, nullptr, nullptr, napi_default, nullptr },};
        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module
demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "libserialport",
.nm_priv = ((void *)0),
.reserved = {
0 },
};

extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(& demoModule);
}

